/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.osdtsystem.functions;

import java.util.ArrayList;
import java.util.List;
import org.osdtsystem.graphs.GraphAccessor;

/**
 * A builder that specifies how to build a string representation of a function
 * given its component parts.
 * @author matthias
 */
public class FunctionStringBuilder {
    static String INDENT = "  ";
    Function<?> function;
    List<Component> components = new ArrayList<Component>();
    List<Parameter> parameters = new ArrayList<Parameter>();
    
    public FunctionStringBuilder(Function<?> function) {
        super();
        this.function = function;
    }

    public void addParameter(String name, Object value) {
        parameters.add(new Parameter(name, value));
    }

    public void addComponent(Object component) {
        addComponent( null,component);
    }

    public void addComponent(String name, Object component) {
        components.add(new Component(component, name));
    }

    public void addSubfunction(Function<?> subfunction) {
        addSubfunction( null,subfunction);
    }

    public void addSubfunction(String name, Function<?> subfunction) {
        components.add(new Subfunction(subfunction, name));
    }

    public void addSubfunctions(Function<?>... subfunctions) {
        for (Function<?> f : subfunctions) {
            addSubfunction(f);
        }
    }

    public void addSubfunctions(List<? extends Function<?>> subfunctions) {
        for (Function<?> f : subfunctions) {
            addSubfunction(f);
        }
    }
    
    public String toString(String prefix1, String prefix) {
        return toString(prefix1, prefix, null, null, null);
    }
    
    public String toString(String prefix1, String prefix, LocalFunction<?, ?> caller, FunctionContext context, GraphAccessor accessor) {
        // Write header
        StringBuilder s = new StringBuilder(prefix1);
        s.append(function.getClass().getSimpleName());

        // Append parameters
        String sep = ": ";
        for (Parameter p : parameters) {
            s.append(sep);
            s.append(p.variable);
            s.append("=");
            s.append(p.value != null ? p.value.toString() : "null");
            sep = " ";
        }

        // Append value
        s.append(" VALUE=");
        String value = null;
        try {
            value = function.functionValue(caller, context, accessor).toString();
        } catch (Exception ex) {
            value = "EXCEPTION";
            // value = ex.getClass().getSimpleName();
        }
        s.append(value != null ? value : "null");

        // Line break
        s.append("\n");

        // Append subcomponents
        for (Component c : components) {
            if (c == null) {
                s.append(prefix + INDENT + "null\n");
            } else if (c instanceof Subfunction) {
                String subprefix = prefix + INDENT;
                String subprefix1 = c.name != null ? subprefix + c.name + "=" : subprefix;
                Subfunction f = (Subfunction) c;
                    s.append(f.function().toString(subprefix1, subprefix, caller, context, accessor));
            }
        }

        // Return string
        return s.toString();
    }

    // Inner classes
    public class Parameter {
        String variable;
        Object value;

        private Parameter(String variable, Object value) {
            this.variable = variable;
            this.value = value;
        }
    }

    public class Component {
        String name;
        Object component;

        public Component(Object component) {
            this(component, null);
        }

        public Component(Object component, String name) {
            this.component = component;
            this.name = name;
        }
    }

    public class Subfunction extends Component {
        public Subfunction(Object component) {
            super(component);
        }

        public Subfunction(Object component, String name) {
            super(component, name);
        }

        public Function<?> function() {
            return (Function<?>) component;
        }
    }
}
